//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

// Includes
#include <mattise.h>
#include <stdint.h>
#include <mem/util.h>
#include <exec/elf.h>
#include <kernio/io.h>
#include <core/portio.h>
#include <process/common.h>
#include <core/intrcommon.h>

// Variables

// the set of custom handlers
static custom_interrupt_handler kCustomHandlers[256];

// Functions

void kIsrCommonHandler( struct stack_layout* stack )
{
    // special case for a GPF - if in virtual mode call the VMM
    if( ( (stack->eflags & 0x20000) && stack->intnum == 13 ) || ( currprocess->vm86task && ( stack->intnum == 13 ) ) )
    {
    	printf( "vmm\n" );
        // kVmm( stack );
        return;
    }

	// if there's a custom handler, call it
	if( kCustomHandlers[stack->intnum] )
	{
		kCustomHandlers[stack->intnum]( stack );
		return;
	}

	// if no handler, print our own message
	printf( "You fail at not crashing this OS. The exception was number %d, process %d\n", stack->intnum, getpid() );
	printf( "Now, go contemplate your failure while this OS loops forever.\n" );
	dprintf( "For those who don't fail and are actually debugging, here's some info:\n" );
	dprintf( "Interrupt number %d, EIP was 0x%x, CS was 0x%x, pid was %d\n", stack->intnum, stack->eip, stack->cs, getpid() );
	dprintf( "Boring stuff:\n" );
	dprintf( "DS: %x ES: %x FS: %x GS: %x SS: %x\n", stack->ds, stack->es, stack->fs, stack->gs, stack->ss );

    // grab the page directory and CR2 value too
    uint32_t cr3, cr2;
    asm volatile( "mov %%cr3,%0" : "=r" (cr3) );
    asm volatile( "mov %%cr2,%0" : "=r" (cr2) );

    // print them out
    printf( "Just thought I'd also mention, CR3 is 0x%x and CR2 is 0x%x\n\n", cr3, cr2 );

    // output stuff to the debug console
    dprintf( "For those who don't fail:\n" );
    dprintf( "CR3: 0x%x, CR2: 0x%x\n", cr3, cr2 );
    dprintf( "error code: 0x%x\n", stack->ecode );
    dprintf( "EIP: %x\n", stack->eip );

    // print the general purpose registers
    dprintf( "GPRs:\n" );
    dprintf( "EAX: %x, EBX: %x, ECX: %x, EDX: %x, ESI: %x, EDI: %x\n", stack->eax, stack->ebx, stack->ecx, stack->edx, stack->esi, stack->edi );
    dprintf( "ESP: %x, EBP: %x\n", stack->esp, stack->ebp );

    // stack base and instruction pointer
    uintptr_t base = stack->ebp;
    uintptr_t eip = stack->eip;

    // check that EBP is valid
    if( cr2 != stack->ebp && stack->ebp )
    {
    	// work through 8 frames
    	size_t i = 0;

		// print info
		dprintf( "Exception %d with EIP=%x, EBP=%x\nCall stack:\n", stack->intnum, eip, base );

		// print it out!
		dprintf( "fault: ", i );
		getkernsym( eip );
		dprintf( " eip=%x, ebp=%x\n", eip, base );

    	// keep going through all eight
    	while( i < 8 )
    	{
			// get the EBP pointer
			if(!IsMapped(0xfffff000, base &~0xFFF0))
				break;
			uintptr_t nextbase = *((uintptr_t*) base);

			// check that it's valid
			if( nextbase == 0 ) break;

			// find the return address
			eip = *((uintptr_t*) (base + sizeof( uintptr_t )));

			// set the next EBP
			base = nextbase;

			// print it
			dprintf( "%d: ", i );
			getkernsym( eip );
			dprintf( " eip=%x, ebp=%x\n", eip, base );

			// next frame
			i++;
    	}
    }

	// kill the task that caused the fault
	if( getpid() > 3 )
	{
		kill( getpid() );
		kSchedule( 0 );
	}

	// loop forever (we faulted)
	while( 1 );
}

void kIrqCommonHandler( struct stack_layout* stack )
{
	// send the EOI
	// this must go first because the handler may end up not returning
	// for instance, the scheduler never actually returns here
	if( stack->intnum >= 40 )
		outb( 0xa0, 0x20 );
	outb( 0x20, 0x20 );

	// if there's a custom handler, call it
	if( kCustomHandlers[stack->intnum] )
		kCustomHandlers[stack->intnum]( stack );
}

// installs a custom interrupt handler
void kInstallCustomIntrHandler( uint32_t intnum, custom_interrupt_handler p )
{
	// set it
	kCustomHandlers[intnum] = p;
}

// removes a custom interrupt handler
void kRemoveCustomIntrHandler( uint32_t intnum )
{
	// remove it
	kCustomHandlers[intnum] = 0;
}

// sets up custom handlers
void kSetupCustomIntrHandlers()
{
	// zero out the array
	memset( kCustomHandlers, 0, sizeof( custom_interrupt_handler ) * 256 );
}
